package com.cskaoyan.javase.object._1getclass;

/**
 * getClass方法
 * 方法的声明:
 * public final native Class<?> getClass();
 * 解释:
 *      1.首先访问权限不需要注意,是public
 *      2.该方法被final修饰,表示方法不能被重写,但是可以继承
 *      3.native表示该方法是一个本地方法,没有方法体
 *      4."Class<?>"处在返回值类型的位置，表示该方法会返回一个Class对象
 *          "<?>"是Java泛型,今天我们直接去掉它,不管它,不会对程序运行造成影响
 *          Java的泛型是后续JDK升级过程中,才添加上去的
 *          Java的泛型是伪泛型,Java的泛型不能影响程序运行,只会影响编译
 *      5."()"方法是无参的,不需要任何传参
 *
 * 方法的作用: 通过一个本地方法的实现，去获取Class对象。
 * 于是在该方法的学习过程中,最重要的问题就是以下:
 *      1.什么是Class对象?
 *      2.Class对象有什么作用?
 *
 * 什么是Class对象?
 * 类加载,是JVM认识了解某个类型的过程,类加载从大的步骤上来说,三步: 加载 --> 连接 --> 初始化
 * 在类加载的同时,为了让程序员也能够在程序运行期间动态获取类型信息,从而完成一些操作
 * JVM在类加载某个类的同时，会在堆上自动创建一个“封装了该类所有类型信息”的对象，被称之为该类的"运行时类对象"。
 * 或者简称为"类对象"
 * 这个类对象就是某个类的Class对象,Class对象当中就封装了某个类的类型信息
 *
 * Class对象的作用?
 * 程序员只需要在程序运行期间,获取某个类的Class对象,那么就能够获取类型信息了
 *
 * 细节问题(补充):
 *      1.JVM是在类加载的"加载"过程,在堆上创建了某个类的Class对象
 *          Class对象是JVM创建的,不是getClass方法创建的
 *          getClass方法不过是获取了这个对象的引用
 *          而获取引用的过程,Java代码自身是实现不了的
 *          所以getClass方法是一个本地方法
 *
 *      2.(重要)类加载只有一次,某个类型的Class对象在堆上是唯一的,独一份的
 *          而不同类型的对象,它们的Class对象也是不同的两个独立对象
 *          根据这样的一个特点,在Java当中getClass方法最常用于,以下场景:
 *          用于判断两个引用所指向的对象,是不是同种类型对象
 *          至于获取Class对象的这个功能,在开发中有其它更好的方式替代
 *
 *      3.Class对象封装了类型信息,那么怎么用呢?
 *          通过Class类的API就可以获取了
 *          这里简单介绍两个方法:
 *          // 获取类的全限定类名
 *          String getName()
 *          // 获取类名
 *          String getSimpleName()
 *          实际上Java的反射技术,就是获取某个类型的Class对象,配合破解私有权限
 *          反射可以称之为"为所欲为"
 *          反射实际上就是Class类的API使用
 *
 *     4.(了解)关于泛型,它会影响编译,建议今天大家不要用.
 *
 * @since 09:58
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s = new Student();
        // getClass方法表示获取堆上Class对象的引用
        Class stuClazz = s.getClass();

        Student s2 = new Student();
        Class stuClazz2 = s2.getClass();

        System.out.println(stuClazz == stuClazz2);

        Teacher t = new Teacher();
        Class teacherClazz = t.getClass();
        System.out.println(teacherClazz == stuClazz);

        System.out.println(judgeType(s, s2));
        System.out.println(judgeType(s, t));

        System.out.println(stuClazz.getName());
        System.out.println(teacherClazz.getSimpleName());

        // Operator '==' cannot be applied to 'java.lang.Class<capture<? extends com.cskaoyan.javase.object._1getclass.Student>>', 'java.lang.Class<capture<? extends com.cskaoyan.javase.object._1getclass.Teacher>>'
        // "=="两边的Class对象的类型当中的泛型不同,影响了程序遍历,所以会报错
        // System.out.println(s.getClass() == t.getClass());
        Class<? extends Student> aClass = s.getClass();
        Class<? extends Teacher> aClass1 = t.getClass();

        Object o1 = "123";
        Object o2 = "123";
        Class<?> aClass2 = o1.getClass();
        Class<?> aClass3 = o2.getClass();
    }

    /**
     * 该方法用于判断两个引用所指向的对象是否是同种类型对象
     * 如果是,返回true
     * 如果不是,返回false
     * 并且该方法在判断类型时,很严格的判断是否是同一种类型
     * 只要不是同种类型,即便有继承父子关系,也会返回false
     *
     * @param o1 用于比较类型的对象1
     * @param o2 用于比较类型的对象2
     * @return boolean
     * @author wuguidong@cskaoyan.onaliyun.com
     * @since 2022/10/11 10:19
     */
    public static boolean judgeType(Object o1, Object o2) {
        if (o1.getClass() == o2.getClass()) {
            // 说明Class对象是同一个对象,于是类型就是同一个类型
            return true;
        }
        // 不是同一个类型
        return false;
    }
}

class Student {
}

class Teacher {
}